Load data and libraries

##################
# LOAD LIBRARIES #
##################
library(tidyverse)
library(Seurat)
library(tidyseurat)
library(cowplot)
library(harmony)
source("../bin/plotting_functions.R")

#########
# PATHS #
#########
input_dir <- "../results/01_QC_st_data/"
result_dir <- "../results/02_integrate_st_data/"
if( isFALSE(dir.exists(result_dir)) ) { dir.create(result_dir,recursive = TRUE) }

#############
# LODA DATA #
#############
DATA <- readRDS(paste0(input_dir,"seuratObj_filtered.RDS"))

Identify Highly Variable Genes (HVG) across samples

################################
# SPLIT INTO SEPERATE DATASETS #
################################
DATA_nested <- DATA %>%
  mutate(batch = orig.ident) %>%
  nest(data = -batch) %>%
  mutate(data = imap(
    data, ~ .x %>%
      NormalizeData(., normalization.method = "LogNormalize", 
                    verbose = FALSE) %>%
      FindVariableFeatures(selection.method = "vst", 
                           nfeatures = 2000, 
                           verbose = FALSE) )) %>%
  mutate(data = setNames(.[["data"]], .$batch))

#########################################
# FIND HIGLY VARIABLE GENES PER DATASET #
#########################################
hvgs_heat <- DATA_nested %>%
  .$data %>%
  map(., ~ .x@assays$RNA@var.features) %>%
  ( function(x){unique(unlist(x)) ->> hvgs_all; return(x)} ) %>%
  # intersect across all samples:
  ( function(x){Reduce(intersect, x) ->> hvgs; return(x)} ) %>% 
  imap_dfc(., ~hvgs_all %in% .x, .id=.y) %>%
  mutate(rownames = hvgs_all) %>%
  column_to_rownames(var = "rownames")

# choose the hvg present in at least two samples:
hig_var <- rownames(hvgs_heat)[rowSums(hvgs_heat)>2]

# remove all VDJ-genes from list of HVG
remove <- str_subset(hig_var, "^IGH|^IGK|^IGL|^TRA|^TRB|^TRD|^TRG")
hig_var <- setdiff(hig_var, remove)

Heatmap of HVG in all samples

pheatmap::pheatmap(t(hvgs_heat * 1), cluster_rows = F, color = c("grey90", "grey20"))

Integration

############
# HARMONY #
###########
DATA <- DATA %>%
  SCTransform(verbose = FALSE) %>%
  FindVariableFeatures(selection.method = "vst",
                      nfeatures = 4000,
                      verbose = FALSE) %>%
  ScaleData(verbose = FALSE, features = hig_var ) %>%
  RunPCA(verbose = FALSE, npcs = 50) %>%
  RunUMAP(dims = 1:50,
          n.components = 2L,
          n.neighbors = 10,
          min.dist = .1,
          spread = .3) 

DATA <- DATA %>%
  RunHarmony(group.by.vars = "orig.ident", 
             reduction.use = "pca",
             dims.use = 1:50, 
             assay.use = "RNA") #%>%

DATA <-   DATA %>%
  RunUMAP(dims = 1:50, 
          n.neighbors = 10,
          min.dist = .1,
          spread = 1,
          repulsion.strength = 1,
          negative.sample.rate = 10,
          n.epochs = 100,
          reduction = "harmony",
          reduction.name = "umapharmony")

Alternative graph based UMAP

integrated <- DATA@reductions$harmony@cell.embeddings
ann <- RcppHNSW::hnsw_build(as.matrix(integrated), distance = "cosine")
knn <- RcppHNSW::hnsw_search(as.matrix(integrated) , ann = ann , k = 15)

UU2 <- uwot::umap(X = NULL,
                 nn_method =  knn,
                 n_components = 2,
                 ret_extra = c("model","fgraph"),
                 verbose = T,
                 min_dist = 0.1,
                 spread = .3,
                 repulsion_strength = 1,
                 negative_sample_rate = 10,
                 n_epochs = 150,
                 n_threads = 8)
dimnames(UU2$embedding) <- list(colnames(DATA),paste0("umap_harmony_knn_", 1:2))
DATA@reductions[["umap_harmony_knn"]] <- CreateDimReducObject(embeddings = UU2$embedding, 
                                                              key = "umap_harmony_knn_")
colnames(DATA@reductions$umap_harmony_knn@cell.embeddings) <- paste0("umap_harmony_knn_", 1:2)
res <- c("umapharmony", "umap_harmony_knn")
p <- map(res, ~plot_clusters.fun(DATA, red=.x, cluster="orig.ident", lable=FALSE, txt_size = 7))
plot_grid(ncol = 2, 
          plotlist = p)

Plot before and after integration

#  dev.new(height=6, width=6.6929133858, noRStudioGD = TRUE)
res <- c("PC", "harmony", "UMAP", "umapharmony")
title <- c("PCA raw data", "PCA Harmony integrated", "UMAP raw data", "UMAP Harmony integrated")
p <- map2(res, title, 
          ~plot_clusters.fun(DATA, 
                             cluster="orig.ident", txt_size = 9,
                             red=.x, lable=FALSE, title=.y))
plot_grid(ncol = 2, 
         plotlist = p)

Plot marker genes

#  dev.new(height=3, width=8, noRStudioGD = TRUE)
################################
# VISUALIZE EXPR. OF KEY GENES #
################################
# col <- c("grey90","grey80","grey60","navy","black")
col <- c("#FFF5F0", "#FEE0D2", "#FCBBA1", "#FC9272", "#FB6A4A", "#EF3B2C", "#CB181D", "#A50F15", "#67000D")
genes <- c("KRT1", "KRT15", "CDH1")
# genes <- c("CD8A", "SFRP2", "CD3E")
# genes <- c("CD8A", "MYOZ2", "CD3E", "EPCAM", "COL6A1", "CD4")

p <- map(genes, ~plot_genes.fun(DATA, .x, col = col, lable = FALSE, red="umapharmony"))
plot_grid(ncol = 3, 
          plotlist = p)

Save seurat object

##################################
# SAVE INTERMEDIATE SEURAT OJECT #
##################################
saveRDS(DATA, paste0(result_dir,"seuratObj_integrated_SCTnorm.RDS"))
# DATA <- readRDS(paste0(result_dir,"seuratObj_integrated.RDS"))

Session info

sessionInfo()
## R version 4.4.1 (2024-06-14)
## Platform: x86_64-apple-darwin20
## Running under: macOS Sonoma 14.7
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/lib/libRblas.0.dylib 
## LAPACK: /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.0
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## time zone: Europe/Stockholm
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] harmony_1.2.0      Rcpp_1.0.12        cowplot_1.1.3      tidyseurat_0.8.0  
##  [5] ttservice_0.4.0    SeuratObject_5.0.2 Seurat_4.4.0       lubridate_1.9.3   
##  [9] forcats_1.0.0      stringr_1.5.1      dplyr_1.1.4        purrr_1.0.2       
## [13] readr_2.1.5        tidyr_1.3.1        tibble_3.2.1       ggplot2_3.5.1     
## [17] tidyverse_2.0.0   
## 
## loaded via a namespace (and not attached):
##   [1] RColorBrewer_1.1-3     rstudioapi_0.16.0      jsonlite_1.8.8        
##   [4] magrittr_2.0.3         spatstat.utils_3.0-4   farver_2.1.2          
##   [7] rmarkdown_2.27         fs_1.6.4               vctrs_0.6.5           
##  [10] ROCR_1.0-11            spatstat.explore_3.2-7 htmltools_0.5.8.1     
##  [13] sass_0.4.9             sctransform_0.4.1      parallelly_1.37.1     
##  [16] KernSmooth_2.23-24     bslib_0.7.0            htmlwidgets_1.6.4     
##  [19] ica_1.0-3              plyr_1.8.9             plotly_4.10.4         
##  [22] zoo_1.8-12             cachem_1.1.0           igraph_2.0.3          
##  [25] mime_0.12              lifecycle_1.0.4        pkgconfig_2.0.3       
##  [28] Matrix_1.7-0           R6_2.5.1               fastmap_1.2.0         
##  [31] fitdistrplus_1.1-11    future_1.33.2          shiny_1.8.1.1         
##  [34] digest_0.6.35          colorspace_2.1-1       patchwork_1.2.0       
##  [37] tensor_1.5             irlba_2.3.5.1          labeling_0.4.3        
##  [40] progressr_0.14.0       fansi_1.0.6            spatstat.sparse_3.0-3 
##  [43] timechange_0.3.0       httr_1.4.7             polyclip_1.10-6       
##  [46] abind_1.4-5            compiler_4.4.1         withr_3.0.0           
##  [49] highr_0.10             MASS_7.3-60.2          tools_4.4.1           
##  [52] lmtest_0.9-40          httpuv_1.6.15          future.apply_1.11.2   
##  [55] goftest_1.2-3          glue_1.7.0             nlme_3.1-164          
##  [58] promises_1.3.0         grid_4.4.1             Rtsne_0.17            
##  [61] cluster_2.1.6          reshape2_1.4.4         generics_0.1.3        
##  [64] gtable_0.3.5           spatstat.data_3.0-4    tzdb_0.4.0            
##  [67] data.table_1.15.4      hms_1.1.3              sp_2.1-4              
##  [70] utf8_1.2.4             spatstat.geom_3.2-9    RcppAnnoy_0.0.22      
##  [73] ggrepel_0.9.5          RANN_2.6.1             pillar_1.9.0          
##  [76] RcppHNSW_0.6.0         spam_2.10-0            later_1.3.2           
##  [79] splines_4.4.1          lattice_0.22-6         survival_3.7-0        
##  [82] deldir_2.0-4           tidyselect_1.2.1       miniUI_0.1.1.1        
##  [85] pbapply_1.7-2          knitr_1.46             gridExtra_2.3         
##  [88] scattermore_1.2        RhpcBLASctl_0.23-42    xfun_0.44             
##  [91] matrixStats_1.3.0      pheatmap_1.0.12        stringi_1.8.4         
##  [94] lazyeval_0.2.2         yaml_2.3.8             evaluate_0.23         
##  [97] codetools_0.2-20       cli_3.6.3              uwot_0.2.2            
## [100] xtable_1.8-4           reticulate_1.37.0      munsell_0.5.1         
## [103] jquerylib_0.1.4        globals_0.16.3         spatstat.random_3.2-3 
## [106] png_0.1-8              parallel_4.4.1         ellipsis_0.3.2        
## [109] dotCall64_1.1-1        listenv_0.9.1          viridisLite_0.4.2     
## [112] scales_1.3.0           ggridges_0.5.6         leiden_0.4.3.1        
## [115] rlang_1.1.4
LS0tCnRpdGxlOiAiSW50ZWdyYXRlIHNwYXRpYWwgZGF0YSIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgpmb3JtYXQ6CiAgaHRtbDoKICAgIGVtYmVkLXJlc291cmNlczogdHJ1ZQogICAgY29kZS1mb2xkOiBzaG93CnBhcmFtczoKICBmaWcucGF0aDogImByIHBhc3RlMChwYXJhbXMkZmlnLnBhdGgpYCIgIy4vRmlndXJlcy8KZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlCi0tLQoKYGBge3IgYmFja2dyb3VuZC1qb2IsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CnNvdXJjZSgiLi4vYmluL3JlbmRlcl93aXRoX2pvYnMuUiIpCgojIHF1YXJ0bwojIHJlbmRlcl9odG1sX3dpdGhfam9iKG91dF9kaXIgPSBsYWJfZGlyKQojIGZzOjpmaWxlX21vdmUocGF0aCA9IGZpbGUsIG5ld19wYXRoID0gcGFzdGUwKGxhYl9kaXIsIGZpbGUpKQoKIyBjdXJyZW50bHkgdXNpbmcgcXVhcnRvIGZvciBnaXRodWIgYW5kIGtuaXRlciBmb3IgaHRtbCBkdSB0byBzb3VyY2UgY29kZSBvcHRpb24gCnJlbmRlcl9naXRfd2l0aF9qb2IoZmlnX3BhdGggPSAiLi9GaWd1cmVzLzAyLyIpCiMgQ2hhbmdlIHRoZSBmaWd1cmUgcGF0aCBmcm9tIC4vRmlndXJlcy8wMy8gdG8gLi4vRmlndXJlcy8wMy86CnN5c3RlbTIoY29tbWFuZCA9ICJzZWQiLCBzdGRvdXQgPSBUUlVFLAogICAgICAgIGFyZ3MgPSBjKCItaSIsICInJyIsIi1lIiwgJ3Mvc3JjPVxcIlxcLi9zcmM9XFwiXFwuXFwuL2cnLAogICAgICAgICAgICAgICAgIHBhc3RlMCgiLi9tZF9maWxlcy8iLCBiYXNlbmFtZSgiLi8wMl9pbnRlZ3JhdGVfc3RfZGF0YS5tZCIpKSkpCgojIGtuaXRlcgprbml0X2h0bWxfd2l0aF9qb2Iob3V0X2RpciA9ICIuLi9sYWJfYm9vay8wMl9pbnRlZ3JhdGVfc3RfZGF0YS8iLCBmaWdfcGF0aCA9ICIuL0ZpZ3VyZXMvMDIvIikKYGBgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGZpZy53aWR0aCA9IDYuNjkyOTEzMzg1OCwKICBmaWcucGF0aCAgICA9IHBhcmFtcyRmaWcucGF0aCwKICBkZXYgICAgICAgICA9IGMoInBuZyIpLAogIGRwaSAgICAgICAgID0gMzAwLAogIGZpZy5hbGlnbiAgID0gImNlbnRlciIsCiAgbWVzc2FnZSAgICAgPSBGQUxTRSwKICB3YXJuaW5nICAgICA9IEZBTFNFLAogIGZpZy5wcm9jZXNzID0gZnVuY3Rpb24oZmlsZW5hbWUpewogICAgICBuZXdfZmlsZW5hbWUgPC0gc3RyaW5ncjo6c3RyX3JlbW92ZShzdHJpbmcgPSBmaWxlbmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIi0xIikKICAgICAgZnM6OmZpbGVfbW92ZShwYXRoID0gZmlsZW5hbWUsIG5ld19wYXRoID0gbmV3X2ZpbGVuYW1lKQogICAgICBpZmVsc2UoZnM6OmZpbGVfZXhpc3RzKG5ld19maWxlbmFtZSksIG5ld19maWxlbmFtZSwgZmlsZW5hbWUpfSkKCiMgc2V0d2QoIn4vd29yay9Ccm9saWRlbnNfd29yay9Qcm9qZWN0cy9TcGF0aWFsX01pY3JvYmlvdGEvc3JjIikKYGBgCgojIyMgTG9hZCBkYXRhIGFuZCBsaWJyYXJpZXMKYGBge3IgTG9hZC1MaWJyYXJ5LWFuZC1kYXRhfQojIyMjIyMjIyMjIyMjIyMjIyMKIyBMT0FEIExJQlJBUklFUyAjCiMjIyMjIyMjIyMjIyMjIyMjIwpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkodGlkeXNldXJhdCkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KGhhcm1vbnkpCnNvdXJjZSgiLi4vYmluL3Bsb3R0aW5nX2Z1bmN0aW9ucy5SIikKCiMjIyMjIyMjIwojIFBBVEhTICMKIyMjIyMjIyMjCmlucHV0X2RpciA8LSAiLi4vcmVzdWx0cy8wMV9RQ19zdF9kYXRhLyIKcmVzdWx0X2RpciA8LSAiLi4vcmVzdWx0cy8wMl9pbnRlZ3JhdGVfc3RfZGF0YS8iCmlmKCBpc0ZBTFNFKGRpci5leGlzdHMocmVzdWx0X2RpcikpICkgeyBkaXIuY3JlYXRlKHJlc3VsdF9kaXIscmVjdXJzaXZlID0gVFJVRSkgfQoKIyMjIyMjIyMjIyMjIwojIExPREEgREFUQSAjCiMjIyMjIyMjIyMjIyMKREFUQSA8LSByZWFkUkRTKHBhc3RlMChpbnB1dF9kaXIsInNldXJhdE9ial9maWx0ZXJlZC5SRFMiKSkKCmBgYAoKIyMjIElkZW50aWZ5IEhpZ2hseSBWYXJpYWJsZSBHZW5lcyAoSFZHKSBhY3Jvc3Mgc2FtcGxlcwpgYGB7ciBGaW5kLUhWR30KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTUExJVCBJTlRPIFNFUEVSQVRFIERBVEFTRVRTICMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKREFUQV9uZXN0ZWQgPC0gREFUQSAlPiUKICBtdXRhdGUoYmF0Y2ggPSBvcmlnLmlkZW50KSAlPiUKICBuZXN0KGRhdGEgPSAtYmF0Y2gpICU+JQogIG11dGF0ZShkYXRhID0gaW1hcCgKICAgIGRhdGEsIH4gLnggJT4lCiAgICAgIE5vcm1hbGl6ZURhdGEoLiwgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwgCiAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKSAlPiUKICAgICAgRmluZFZhcmlhYmxlRmVhdHVyZXMoc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmZlYXR1cmVzID0gMjAwMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkgKSkgJT4lCiAgbXV0YXRlKGRhdGEgPSBzZXROYW1lcyguW1siZGF0YSJdXSwgLiRiYXRjaCkpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIEZJTkQgSElHTFkgVkFSSUFCTEUgR0VORVMgUEVSIERBVEFTRVQgIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpodmdzX2hlYXQgPC0gREFUQV9uZXN0ZWQgJT4lCiAgLiRkYXRhICU+JQogIG1hcCguLCB+IC54QGFzc2F5cyRSTkFAdmFyLmZlYXR1cmVzKSAlPiUKICAoIGZ1bmN0aW9uKHgpe3VuaXF1ZSh1bmxpc3QoeCkpIC0+PiBodmdzX2FsbDsgcmV0dXJuKHgpfSApICU+JQogICMgaW50ZXJzZWN0IGFjcm9zcyBhbGwgc2FtcGxlczoKICAoIGZ1bmN0aW9uKHgpe1JlZHVjZShpbnRlcnNlY3QsIHgpIC0+PiBodmdzOyByZXR1cm4oeCl9ICkgJT4lIAogIGltYXBfZGZjKC4sIH5odmdzX2FsbCAlaW4lIC54LCAuaWQ9LnkpICU+JQogIG11dGF0ZShyb3duYW1lcyA9IGh2Z3NfYWxsKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gInJvd25hbWVzIikKCiMgY2hvb3NlIHRoZSBodmcgcHJlc2VudCBpbiBhdCBsZWFzdCB0d28gc2FtcGxlczoKaGlnX3ZhciA8LSByb3duYW1lcyhodmdzX2hlYXQpW3Jvd1N1bXMoaHZnc19oZWF0KT4yXQoKIyByZW1vdmUgYWxsIFZESi1nZW5lcyBmcm9tIGxpc3Qgb2YgSFZHCnJlbW92ZSA8LSBzdHJfc3Vic2V0KGhpZ192YXIsICJeSUdIfF5JR0t8XklHTHxeVFJBfF5UUkJ8XlRSRHxeVFJHIikKaGlnX3ZhciA8LSBzZXRkaWZmKGhpZ192YXIsIHJlbW92ZSkKYGBgCgojIyMgSGVhdG1hcCBvZiBIVkcgaW4gYWxsIHNhbXBsZXMKYGBge3IgMDJhX0hWR19oZWF0bWFwLCBmaWcud2lkdGg9MTB9CnBoZWF0bWFwOjpwaGVhdG1hcCh0KGh2Z3NfaGVhdCAqIDEpLCBjbHVzdGVyX3Jvd3MgPSBGLCBjb2xvciA9IGMoImdyZXk5MCIsICJncmV5MjAiKSkKYGBgCgoKIyMjIEludGVncmF0aW9uCmBgYHtyIEludGVncmF0aW9ufQojIyMjIyMjIyMjIyMKIyBIQVJNT05ZICMKIyMjIyMjIyMjIyMKREFUQSA8LSBEQVRBICU+JQogIFNDVHJhbnNmb3JtKHZlcmJvc2UgPSBGQUxTRSkgJT4lCiAgRmluZFZhcmlhYmxlRmVhdHVyZXMoc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiLAogICAgICAgICAgICAgICAgICAgICAgbmZlYXR1cmVzID0gNDAwMCwKICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkgJT4lCiAgU2NhbGVEYXRhKHZlcmJvc2UgPSBGQUxTRSwgZmVhdHVyZXMgPSBoaWdfdmFyICkgJT4lCiAgUnVuUENBKHZlcmJvc2UgPSBGQUxTRSwgbnBjcyA9IDUwKSAlPiUKICBSdW5VTUFQKGRpbXMgPSAxOjUwLAogICAgICAgICAgbi5jb21wb25lbnRzID0gMkwsCiAgICAgICAgICBuLm5laWdoYm9ycyA9IDEwLAogICAgICAgICAgbWluLmRpc3QgPSAuMSwKICAgICAgICAgIHNwcmVhZCA9IC4zKSAKCkRBVEEgPC0gREFUQSAlPiUKICBSdW5IYXJtb255KGdyb3VwLmJ5LnZhcnMgPSAib3JpZy5pZGVudCIsIAogICAgICAgICAgICAgcmVkdWN0aW9uLnVzZSA9ICJwY2EiLAogICAgICAgICAgICAgZGltcy51c2UgPSAxOjUwLCAKICAgICAgICAgICAgIGFzc2F5LnVzZSA9ICJSTkEiKSAjJT4lCgpEQVRBIDwtICAgREFUQSAlPiUKICBSdW5VTUFQKGRpbXMgPSAxOjUwLCAKICAgICAgICAgIG4ubmVpZ2hib3JzID0gMTAsCiAgICAgICAgICBtaW4uZGlzdCA9IC4xLAogICAgICAgICAgc3ByZWFkID0gMSwKICAgICAgICAgIHJlcHVsc2lvbi5zdHJlbmd0aCA9IDEsCiAgICAgICAgICBuZWdhdGl2ZS5zYW1wbGUucmF0ZSA9IDEwLAogICAgICAgICAgbi5lcG9jaHMgPSAxMDAsCiAgICAgICAgICByZWR1Y3Rpb24gPSAiaGFybW9ueSIsCiAgICAgICAgICByZWR1Y3Rpb24ubmFtZSA9ICJ1bWFwaGFybW9ueSIpCmBgYAoKIyMjIEFsdGVybmF0aXZlIGdyYXBoIGJhc2VkIFVNQVAKYGBge3IgYWx0ZXJuYXRpdmUtd2F5LW9mLVVNQVB9CmludGVncmF0ZWQgPC0gREFUQUByZWR1Y3Rpb25zJGhhcm1vbnlAY2VsbC5lbWJlZGRpbmdzCmFubiA8LSBSY3BwSE5TVzo6aG5zd19idWlsZChhcy5tYXRyaXgoaW50ZWdyYXRlZCksIGRpc3RhbmNlID0gImNvc2luZSIpCmtubiA8LSBSY3BwSE5TVzo6aG5zd19zZWFyY2goYXMubWF0cml4KGludGVncmF0ZWQpICwgYW5uID0gYW5uICwgayA9IDE1KQoKVVUyIDwtIHV3b3Q6OnVtYXAoWCA9IE5VTEwsCiAgICAgICAgICAgICAgICAgbm5fbWV0aG9kID0gIGtubiwKICAgICAgICAgICAgICAgICBuX2NvbXBvbmVudHMgPSAyLAogICAgICAgICAgICAgICAgIHJldF9leHRyYSA9IGMoIm1vZGVsIiwiZmdyYXBoIiksCiAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IFQsCiAgICAgICAgICAgICAgICAgbWluX2Rpc3QgPSAwLjEsCiAgICAgICAgICAgICAgICAgc3ByZWFkID0gLjMsCiAgICAgICAgICAgICAgICAgcmVwdWxzaW9uX3N0cmVuZ3RoID0gMSwKICAgICAgICAgICAgICAgICBuZWdhdGl2ZV9zYW1wbGVfcmF0ZSA9IDEwLAogICAgICAgICAgICAgICAgIG5fZXBvY2hzID0gMTUwLAogICAgICAgICAgICAgICAgIG5fdGhyZWFkcyA9IDgpCmRpbW5hbWVzKFVVMiRlbWJlZGRpbmcpIDwtIGxpc3QoY29sbmFtZXMoREFUQSkscGFzdGUwKCJ1bWFwX2hhcm1vbnlfa25uXyIsIDE6MikpCkRBVEFAcmVkdWN0aW9uc1tbInVtYXBfaGFybW9ueV9rbm4iXV0gPC0gQ3JlYXRlRGltUmVkdWNPYmplY3QoZW1iZWRkaW5ncyA9IFVVMiRlbWJlZGRpbmcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleSA9ICJ1bWFwX2hhcm1vbnlfa25uXyIpCmNvbG5hbWVzKERBVEFAcmVkdWN0aW9ucyR1bWFwX2hhcm1vbnlfa25uQGNlbGwuZW1iZWRkaW5ncykgPC0gcGFzdGUwKCJ1bWFwX2hhcm1vbnlfa25uXyIsIDE6MikKYGBgCgpgYGB7ciwgMDJiX1VNQVBfb3B0aW9ucywgZmlnLmFzcD01LzEwfQpyZXMgPC0gYygidW1hcGhhcm1vbnkiLCAidW1hcF9oYXJtb255X2tubiIpCnAgPC0gbWFwKHJlcywgfnBsb3RfY2x1c3RlcnMuZnVuKERBVEEsIHJlZD0ueCwgY2x1c3Rlcj0ib3JpZy5pZGVudCIsIGxhYmxlPUZBTFNFLCB0eHRfc2l6ZSA9IDcpKQpwbG90X2dyaWQobmNvbCA9IDIsIAogICAgICAgICAgcGxvdGxpc3QgPSBwKQpgYGAKCiMjIyBQbG90IGJlZm9yZSBhbmQgYWZ0ZXIgaW50ZWdyYXRpb24KYGBge3IgMDJjX1Bsb3RfZGltX3JlZHVjdGlvbiwgZmlnLmhlaWdodD02fQojICBkZXYubmV3KGhlaWdodD02LCB3aWR0aD02LjY5MjkxMzM4NTgsIG5vUlN0dWRpb0dEID0gVFJVRSkKcmVzIDwtIGMoIlBDIiwgImhhcm1vbnkiLCAiVU1BUCIsICJ1bWFwaGFybW9ueSIpCnRpdGxlIDwtIGMoIlBDQSByYXcgZGF0YSIsICJQQ0EgSGFybW9ueSBpbnRlZ3JhdGVkIiwgIlVNQVAgcmF3IGRhdGEiLCAiVU1BUCBIYXJtb255IGludGVncmF0ZWQiKQpwIDwtIG1hcDIocmVzLCB0aXRsZSwgCiAgICAgICAgICB+cGxvdF9jbHVzdGVycy5mdW4oREFUQSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcj0ib3JpZy5pZGVudCIsIHR4dF9zaXplID0gOSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWQ9LngsIGxhYmxlPUZBTFNFLCB0aXRsZT0ueSkpCnBsb3RfZ3JpZChuY29sID0gMiwgCiAgICAgICAgIHBsb3RsaXN0ID0gcCkKYGBgCgojIyMgUGxvdCBtYXJrZXIgZ2VuZXMKYGBge3IgMDJkX3Bsb3RfbWFya2VyX2dlbmVzLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD04fQojICBkZXYubmV3KGhlaWdodD0zLCB3aWR0aD04LCBub1JTdHVkaW9HRCA9IFRSVUUpCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgVklTVUFMSVpFIEVYUFIuIE9GIEtFWSBHRU5FUyAjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgY29sIDwtIGMoImdyZXk5MCIsImdyZXk4MCIsImdyZXk2MCIsIm5hdnkiLCJibGFjayIpCmNvbCA8LSBjKCIjRkZGNUYwIiwgIiNGRUUwRDIiLCAiI0ZDQkJBMSIsICIjRkM5MjcyIiwgIiNGQjZBNEEiLCAiI0VGM0IyQyIsICIjQ0IxODFEIiwgIiNBNTBGMTUiLCAiIzY3MDAwRCIpCmdlbmVzIDwtIGMoIktSVDEiLCAiS1JUMTUiLCAiQ0RIMSIpCiMgZ2VuZXMgPC0gYygiQ0Q4QSIsICJTRlJQMiIsICJDRDNFIikKIyBnZW5lcyA8LSBjKCJDRDhBIiwgIk1ZT1oyIiwgIkNEM0UiLCAiRVBDQU0iLCAiQ09MNkExIiwgIkNENCIpCgpwIDwtIG1hcChnZW5lcywgfnBsb3RfZ2VuZXMuZnVuKERBVEEsIC54LCBjb2wgPSBjb2wsIGxhYmxlID0gRkFMU0UsIHJlZD0idW1hcGhhcm1vbnkiKSkKcGxvdF9ncmlkKG5jb2wgPSAzLCAKICAgICAgICAgIHBsb3RsaXN0ID0gcCkKYGBgCgojIyBTYXZlIHNldXJhdCBvYmplY3QKYGBge3Igc2F2ZS1TZXVyYXRPYmp9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTQVZFIElOVEVSTUVESUFURSBTRVVSQVQgT0pFQ1QgIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnNhdmVSRFMoREFUQSwgcGFzdGUwKHJlc3VsdF9kaXIsInNldXJhdE9ial9pbnRlZ3JhdGVkX1NDVG5vcm0uUkRTIikpCiMgREFUQSA8LSByZWFkUkRTKHBhc3RlMChyZXN1bHRfZGlyLCJzZXVyYXRPYmpfaW50ZWdyYXRlZC5SRFMiKSkKYGBgCgojIyMgU2Vzc2lvbiBpbmZvCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAo=